﻿using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Media;
using TextEditor.TextBoxInner.Utils;

namespace TextEditor.TextBoxInner.DrawingElements
{
	internal class TextLine : DrawingVisual
	{
		private readonly List<ushort> _glyphIndexes = new List<ushort>(100);
		private readonly List<double> _advanceWidths = new List<double>(100);

		private RenderedInfo _renderedInfo;

		public TextLine()
		{
			VisualTextRenderingMode = TextRenderingMode.Aliased;
		}

		public bool IsVisible
		{
			get { return _renderedInfo != null; }
		}

		public int LineIndex
		{
			get { return _renderedInfo.LineIndex; }
		}

		public void SetTop(double topOffset)
		{
			VisualOffset = new Vector(0, topOffset);
		}

		public double GetTop()
		{
			return VisualOffset.Y;
		}

		public void SetContent(RenderedInfo renderedInfo)
		{
			if (_renderedInfo == renderedInfo)
				return;

			_renderedInfo = renderedInfo;
			Draw();
		}

		public override string ToString()
		{
			return IsVisible ? "Line: " + LineIndex : "Invisible";
		}

		private void Draw()
		{
			DrawingContext drawing = RenderOpen();

			try
			{
				if (_renderedInfo != null)
					DrawLine(drawing, _renderedInfo);
			}
			finally
			{
				drawing.Close();
			}
		}

		private void DrawLine(DrawingContext drawingContext, RenderedInfo info)
		{
			int chCount = 0;

			for (int index = 0; index < info.Styles.Count; index++)
			{
				UfeTextStyle ufeTextStyle = info.Styles[index];
				DrawText(drawingContext, ufeTextStyle.Text, ref chCount, ufeTextStyle.Brush, new Point(chCount * FontHelper.FontWidth, 0));
			}
		}

		internal string TextAsString
		{
			get { return _renderedInfo.Styles.Aggregate(new StringBuilder(), (builder, style) => builder.Append(style.Text)).ToString(); }
		}

		private void DrawText(DrawingContext drawingContext, string text, ref int chCount, Brush brush, Point location)
		{
			if (string.IsNullOrEmpty(text))
				return;

			_glyphIndexes.Clear();
			_advanceWidths.Clear();

			for (int n = 0, max = text.Length; n < max; n++)
			{
				char ch = text[n];

				if (ch == '\t')
				{
					int count = 4 - chCount % 4;
					chCount += count;

					for (int marker = 0; marker < count; marker++)
					{
						_glyphIndexes.Add(FontHelper.GlyphSpaceIndex);
						_advanceWidths.Add(FontHelper.FontWidth);
					}
				}
				else
				{
					chCount++;

					ushort glyphIndex;
					if (!FontHelper.GlyphTypeface.CharacterToGlyphMap.TryGetValue(ch, out glyphIndex))
						glyphIndex = FontHelper.GlyphUnknownIndex;

					_glyphIndexes.Add(glyphIndex);
					_advanceWidths.Add(FontHelper.FontWidth);
				}
			}

			GlyphRun glyphRun = new GlyphRun(FontHelper.GlyphTypeface, 0, false, FontHelper.FontSize,
				_glyphIndexes.ToArray(), new Point(location.X, location.Y + FontHelper.FontSize), _advanceWidths.ToArray(), null, null, null, null,
				null, null);

			drawingContext.DrawGlyphRun(brush, glyphRun);
		}
	}
}
